#pragma once
#include "rpc_requestor.hpp"

namespace RPC
{
    namespace Client
    {
        // 客服注册端
        class Provider
        {
        public:
            using Ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::Ptr &requestor = std::make_shared<Requestor>())
                : _requestor(requestor)
            {
            }

            bool registorMethod(const BaseConnection::Ptr &conn, const Address &host, const std::string &method)
            {
                ServiceRequest::Ptr req = MessageFactory::create<ServiceRequest>();
                req->setHost(host);
                req->setId(UUID::uuid());
                req->setMethod(method);
                req->setOpType(ServiceOpType::SERVICE_REGISTER);
                req->setType(MType::REQ_SERVICE);
                BaseMessage::Ptr res;
                bool sendret = _requestor->send(conn, req, res);
                if (sendret == false)
                {
                    ERROR_LOG("消息发送失败！");
                    return false;
                }
                ServiceResponse::Ptr resp = std::dynamic_pointer_cast<ServiceResponse>(res);
                if (resp == nullptr)
                {
                    ERROR_LOG("向下转型失败！");
                    return false;
                }
                if (resp->rCode() != RCode::RCODE_OK)
                {
                    ERROR_LOG("注册方法错误：%s", errReason(resp->rCode()).c_str());
                    return false;
                }
                return true;
            }

        private:
            Requestor::Ptr _requestor;
        };

        // 客服发现端
        class MethodHost
        {
        public:
            using Ptr = std::shared_ptr<MethodHost>;
            MethodHost()
                : _index(0) {}

            MethodHost(const std::vector<Address> &hosts)
                : _index(0), _hosts(hosts)
            {
            }

            void append(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.emplace_back(host);
            }

            // void append(const std::vector<Address> &hosts)
            // {
            //     std::unique_lock<std::mutex> lock(_mutex);
            //     for (const auto &x : hosts)
            //         _hosts.emplace_back(x);
            // }

            void removeHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for (auto it = _hosts.begin(); it != _hosts.end(); it++)
                {
                    if (*it == host)
                    {
                        _hosts.erase(it);
                        break;
                        // 一定要退出，否则会迭代器失效
                    }
                }
            }

            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _hosts[_index++ % _hosts.size()];
            }

            bool empty()
            {
                return _hosts.empty();
            }

        private:
            std::mutex _mutex;
            size_t _index;
            std::vector<Address> _hosts;
        };

        class Discover
        {
        public:
            using Ptr = std::shared_ptr<Discover>;
            using shutConnCallback = std::function<void(const Address&)>;
            Discover(const Requestor::Ptr &req, shutConnCallback cb)
                : _requestor(req)
                , _shutcb(cb)
            {
            }

            // 进行服务发现
            bool serviceDiscover(const BaseConnection::Ptr &conn, const std::string &method, Address &host)
            {
                auto it = _method_host.find(method);
                if (it != _method_host.end())
                {
                    if (it->second->empty() == false)
                    {
                        host = it->second->chooseHost();
                        return true;
                    }
                }
                ServiceRequest::Ptr req = MessageFactory::create<ServiceRequest>();
                req->setId(UUID::uuid());
                req->setMethod(method);
                req->setOpType(ServiceOpType::SERVICE_DISCOVERY);
                req->setType(MType::REQ_SERVICE);
                BaseMessage::Ptr resp;
                bool sendret = _requestor->send(conn, req, resp);
                if (sendret == false)
                {
                    ERROR_LOG("消息发送失败！");
                    return false;
                }
                ServiceResponse::Ptr sresp = std::dynamic_pointer_cast<ServiceResponse>(resp);
                if (sresp == nullptr)
                {
                    ERROR_LOG("向下转型失败！");
                    return false;
                }
                if (sresp->rCode() != RCode::RCODE_OK)
                {
                    ERROR_LOG("返回值不正确！");
                    return false;
                }
                std::vector<Address> hosts;
                hosts = sresp->host();
                if (hosts.empty())
                {
                    ERROR_LOG("当前没有提供方法的服务器！");
                    return false;
                }

                // 上锁，添加服务主机到_method_host;
                std::unique_lock<std::mutex> lock(_mutex);
                MethodHost::Ptr mhost = std::make_shared<MethodHost>(hosts);
                host = mhost->chooseHost();
                _method_host[method] = mhost;
                return true;
            }

            // 处理服务上线下线,这个接口提供给dispather模块进行消息的分发。
            void onMessage(const BaseConnection::Ptr &conn, const ServiceRequest::Ptr &req)
            {
                // ERROR_LOG("准备执行关闭回调函数!");
                std::unique_lock<std::mutex> lock(_mutex);
                std::string method = req->method();
                if (req->opType() == ServiceOpType::SERVICE_ONLINE)
                {
                    // 1、服务上线，添加方法到对应的方法集
                    auto it = _method_host.find(method);
                    if (it == _method_host.end())
                    {
                        auto method_host = std::make_shared<MethodHost>();
                        method_host->append(req->host());
                        _method_host[method] = method_host;
                    }
                    else
                    {
                        _method_host[method]->append(req->host());
                    }
                }
                else if (req->opType() == ServiceOpType::SERVICE_OFFLINE)
                {
                    // 2、服务下线，从方法集中删除主机
                    auto it = _method_host.find(method);
                    if (it == _method_host.end())
                        return;
                    _method_host[method]->removeHost(req->host());
                    // 回调
                    // ERROR_LOG("hsot:%s", req->host().first.c_str());
                    // ERROR_LOG("port:%d", req->host().second);
                    _shutcb(req->host());
                    // ERROR_LOG("回调执行完成！");
                }
            }

        private:
            std::mutex _mutex;
            shutConnCallback _shutcb;
            std::unordered_map<std::string, MethodHost::Ptr> _method_host;
            Requestor::Ptr _requestor;
        };
    }
}